home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
smaltalk
/
stv.lha
/
STV
/
ISA
/
carolina
/
tictacto.prj
< prev
next >
Wrap
Text File
|
1993-07-23
|
12KB
|
479 lines
"
******************************************************************************
Project : TicTacToe
Date : Oct 10, 1989
Time : 21:43:42
Introduction
============
"Interactive TicTacToe game
based on W. LaLonde & J. Pugh
J.O.O.P Sept/Oct 1989 pp 57-66"
Invoked By:
===========
InteractiveTicTacToe example1
Description
===========
Classes :
TileDispatcher TilePane TicTacToeGame
InteractiveTicTacToe
Methods :
******************************************************************************
"!
"Initialize"
InteractiveTioTacToe example1
!
Dispatcher subclass: #TileDispatcher
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries:
'FunctionKeys CharacterConstants '!
SubPane subclass: #TilePane
instanceVariableNames:
'tiles selectedTile enterTileSelector exitTileSelector selectTileSelector '
classVariableNames: ''
poolDictionaries: ''!
Object subclass: #TicTacToeGame
instanceVariableNames:
'winningLines board lastPlayer winner winningLine '
classVariableNames: ''
poolDictionaries: ''!
TicTacToeGame subclass: #InteractiveTicTacToe
instanceVariableNames:
'gameRectangle tiles '
classVariableNames: ''
poolDictionaries: ''!
!TileDispatcher class methods ! !
!TileDispatcher methods !
isControlActive
"Answer true if mouse is over the pane or
the mouse is down"
^super isControlActive or:
[Terminal mouseSelectOn]!
processFunctionKey: aCharacter
"Process function characters"
aCharacter == SelectFunction
ifTrue: [^pane mouseDown].
aCharacter == EndSelectFunction
ifTrue: [pane mouseUp.
^Terminal initialize
"clear all characters"].
aCharacter == SetLoc
ifTrue: [^Terminal mouseSelectOn
ifTrue: [pane mouseMove]].
aCharacter == ScrollUpFunction
ifTrue: [^super processFunctionKey: PaneMenuRequest].
aCharacter == ScrollDownFunction
ifTrue: [^nil].
super processFunctionKey: aCharacter
"otherwise beep"!
processMouseEvent: aCharacter
"process mouse characters"
self processFunctionKey: aCharacter! !
!TilePane class methods !
example1
" TilePane example1 "
super new initialize! !
!TilePane methods !
activeTile
"Return the tile containing the cursor or nil"
^tiles
detect: [:aTile | (frame scaleTo: aTile)
containsPoint: Cursor offset]
ifNone: [nil]!
defaultDispatcherClass
"Answer the default dispatcher class TileDispatcher"
^TileDispatcher!
enterTile
"Tell model the mouse has entered the tile
default to reversing the tile"
(model respondsTo: enterTileSelector)
ifTrue: [model perform: enterTileSelector
with: selectedTile]
ifFalse: [self reverseTile].!
exitTile
"Tell model the mouse has exited the tile
default to reversing the tile"
(model respondsTo: exitTileSelector)
ifTrue: [model perform: exitTileSelector
with: selectedTile]
ifFalse: [self reverseTile].!
initialize
"Initialize the pane"
super initialize.
curFont := SysFont!
mouseDown
"mouse down in the pane @ cursor
enter selected tile if any"
(selectedTile := self activeTile) isNil
ifFalse: [self enterTile].!
mouseMove
"everytime the mouse moves, notify the model
if boundary is crossed or significant event occurs"
| newTile |
(newTile := self activeTile) == selectedTile
ifTrue: [^self].
selectedTile isNil ifFalse: [self exitTile].
(selectedTile := newTile) isNil ifFalse: [self enterTile].!
mouseUp
"mouse button released at the pane @ cursor
inform model"
(selectedTile := self activeTile) isNil
ifFalse: [self exitTile;
selectTile.
selectedTile := nil].!
onMouseEnteringTile: aSymbol
enterTileSelector := aSymbol!
onMouseExitingTile: aSymbol
exitTileSelector := aSymbol!
onMouseSelectingTile: aSymbol
selectTileSelector := aSymbol!
reverseTile
"reverse color of tile"
| tileArea |
tileArea := frame scaleTo: selectedTile.
tileArea := tileArea origin truncated corner:
tileArea corner truncated.
Display gray: (tileArea intersect: frame)!
selectTile
"Tell model the mouse has selected the tile"
(model respondsTo: selectTileSelector)
ifTrue: [model perform: selectTileSelector
with: selectedTile].!
showWindow
"Display the pane and get update from model"
self border.
tiles := model perform: name with: frame!
update
"get latest data from model"
self showWindow! !
!TicTacToeGame class methods !
example1
" TicTacToeGame example1 "
"Play game directly without dispatcher or pane"
| aGame firstPlayer response |
aGame := TicTacToeGame new.
firstPlayer := (self yesNoPrompt: 'Does the X player wish to start?')
ifTrue: [#X]
ifFalse: [#O].
aGame nextPlayer: firstPlayer.
(aGame gameOver) whileFalse:[
response := Prompter
prompt:'Player ', aGame nextPlayer,
'please provide the next board coordinate' "as a point"
defaultExpresion: '1@1'.
(aGame isLegalFor: (aGame nextPlayer) toPlayAt: response)
ifTrue: [aGame play: (aGame nextPlayer) at: response]
ifFalse: [(self yesNoPrompt: 'Bad move, do you want to continue?')
ifFalse: [^self]]].
Menu message: (aGame winner == #NoOne
ifTrue: ['A tie']
ifFalse: ['Player', aGame winner , 'wins'])!
example2
| aGame |
aGame := TicTacToeGame new.
WinningLines inspect!
new
^super new initialize!
yesNoPrompt: queryString
^((Menu
labels: queryString, '\yes\no' withCrs
lines: #(1)
selectors: #(no yes no))
popUpAt: Cursor offset) == #yes! !
!TicTacToeGame methods !
at: aCoordinate
| xval yval val|
xval := aCoordinate x.
yval := aCoordinate y.
val := 3*(xval - 1) + yval.
^board at: val.!
at: aCoordinate put: aValue
^board at: 3*((aCoordinate x) - 1)
+(aCoordinate y)
put: aValue!
gameOver
"Returns true if game is over"
self winner == #NoOne
ifFalse: [^true].
board do: [:square | square == #Empty
ifTrue: [^false]].
^true!
initialize
"Generate all possible winning triples"
| square1 square2 square3 |
winningLines :=
#((0 1 2) (3 4 5) (6 7 8) "rows"
(0 3 6) (1 4 7) (2 5 8) "columns"
(0 4 8) (2 4 6) "diagonals")
collect: [:triple |
square1 := triple first.
square2 := triple at: 2.
square3 := triple last.
Array
with: (square1 //3)@(square1 \\3) +1
with: (square2 //3)@(square2 \\3) +1
with: (square3 //3)@(square3 \\3) +1].
"instance initialization"
board := (Array new: 9) atAllPut: #Empty; yourself.
winner := nil.
lastPlayer := #NoOne.!
isLegalFor: aPlayer toPlayAt: aCoordinate
(aCoordinate x between: 1 and: 3) &
(aCoordinate y between: 1 and: 3) ifFalse: [^false].
(self at: aCoordinate) == #Empty ifFalse: [^false].
(self winner == #NoOne) ifFalse: [^false].
^lastPlayer ~= aPlayer!
nextPlayer
^lastPlayer == #X ifTrue: [#O]
ifFalse: [#X]!
nextPlayer: aPlayer
(lastPlayer == #NoOne)&((aPlayer == #X)|(aPlayer == #O))
ifFalse: [self error: 'initialize with X or O'].
lastPlayer := aPlayer == #X
ifTrue: [#O]
ifFalse: [#X].
^aPlayer!
play: aPlayer at: aCoordinate
(self isLegalFor: aPlayer toPlayAt: aCoordinate)
ifTrue: [self at: aCoordinate put: aPlayer]
ifFalse: [self error: 'You cannot play at', aCoordinate printString].
lastPlayer := aPlayer!
winner
"Returns X, O or NoOne"
| coordinate1 coordinate2 coordinate3
square1 square2 square3 |
winner ~~ nil ifTrue: [^winner].
"consider each possible line"
winningLines do: [:triple |
coordinate1 := triple first.
coordinate2 := triple at: 2.
coordinate3 := triple last.
square1 := self at: coordinate1.
square2 := self at: coordinate2.
square3 := self at: coordinate3.
(square1 ~~ #Empty)&(square1 == square2)&(square2==square3)
ifTrue: [
winner := square1.
winningLine := Array
with: coordinate1
with: coordinate2
with: coordinate3.
^winner]].
"there is no winner"
^#NoOne!
winningLine
^winningLine! !
!InteractiveTicTacToe class methods !
example1
" InteractiveTicTacToe example1 "
self new open! !
!InteractiveTicTacToe methods !
drawTile: aTile
"Draw a specified tile"
| aCoordinate scaledRectangle tileType aPen xBranchSize |
aCoordinate := tiles at: aTile.
scaledRectangle := (gameRectangle scaleTo: aTile)
insetBy: 3@3.
tileType:= self at: aCoordinate.
(aPen := Pen new)
defaultNib: 4@4;
place: scaledRectangle center truncated.
xBranchSize := (scaledRectangle width min:
scaledRectangle height) // 2.
Display white: scaledRectangle.
tileType == #X ifTrue:[
aPen turn: 45.
4 timesRepeat: [
aPen go: xBranchSize;
go: xBranchSize negated;
turn: 90]].
tileType == #O ifTrue: [
aPen ellipse: xBranchSize * 3 // 4
aspect: 1]!
drawWinnerLine
"draw a line from center of outermost tiles of
winning squares"
| firstTile lastTile |
firstTile := tiles keyAtValue: self winningLine first.
lastTile := tiles keyAtValue: self winningLine last.
Pen new
defaultNib: 5@5;
place: (gameRectangle scaleTo: firstTile) center;
goto: (gameRectangle scaleTo: lastTile) center.!
initialize
"Set up new game"
super initialize.
"Set up tiles"
tiles := Dictionary new.
1 to: 3 do: [:row |
1 to: 3 do: [:column |
tiles
at: (((column - 1)/3)@((row - 1)/3)
extent: (1/3)@(1/3))
put: row@column]].!
label
"Answer the label for the game window"
| theWinner |
self gameOver ifFalse:[^'Player',self nextPlayer,'''s move'].
theWinner := self winner.
theWinner == #NoOne ifTrue:[^'Nobody one'].
theWinner == #X ifTrue:[^'X won!!'].
theWinner == #O ifTrue:[^'O won!!'].
^'TicTacToe'.!
menu
"Answer the menu for the TilePane"
^Menu labels: 'restart'
lines: #()
selectors: #restart!
mouseEnteringTile: aTile
"Reverse the tile"
| scaledRectangle |
scaledRectangle := (gameRectangle scaleTo: aTile)
insetBy: 3@3.
Display gray: scaledRectangle!
mouseExitingTile: aTile
"reverse the Tile"
self mouseEnteringTile: aTile.!
mouseSelectingTile: aTile
" update the game and pane with selected tile"
| aCoordinate row column |
self gameOver ifTrue: [^self].
aCoordinate := tiles at: aTile.
(self isLegalFor: self nextPlayer toPlayAt: aCoordinate)
ifTrue: [self play: self nextPlayer at: aCoordinate]
ifFalse: [6 timesRepeat: [Display reverse.
5000 timesRepeat:[]].
^self].
(self gameOver and: [self winner ~~ #NoOne])
ifTrue: [self changed: #tiles:]
ifFalse: [self drawTile: aTile].
self changed: #label!
open
"Start up new game"
| topPane |
topPane := TopPane new
label: self label;
model: self;
addSubpane: (TilePane new
model: self;
name: #tiles:;
onMouseEnteringTile: #mouseEnteredTile:;
onMouseExitingTile: #mouseExitingTile:;
onMouseSelectingTile: #mouseSelectingTile:;
menu: #menu;
yourself);
yourself.
topPane dispatcher open scheduleWindow.!
restart
"Restart the game"
self initialize.
self changed: #tiles:; changed: #label!
tiles: aRectangle
"Draw the background on the tiling plane framed
at aRectangle and answer a collection of relative
rectangles for the tiles"
| beginningTile endingTile |
gameRectangle := aRectangle.
Display gray:gameRectangle.
tiles keysDo: [:aTile | self drawTile: aTile].
self winner == #NoOne ifFalse:
[self drawWinnerLine].
^tiles keys.! !
"construct application"
((Smalltalk at: #Application ifAbsent: [])
isKindOf: Class) ifTrue: [
((Smalltalk at: #Application) for:'TicTacToe')
addClass: TileDispatcher;
addClass: TilePane;
addClass: TicTacToeGame;
addClass: InteractiveTicTacToe;
comments: '"Interactive TicTacToe game
based on W. LaLonde & J. Pugh
J.O.O.P Sept/Oct 1989 pp 57-66"
';
initCode: 'InteractiveTioTacToe example1
';
finalizeCode: nil;
startUpCode: 'InteractiveTicTacToe example1
']!